﻿using System;
using System.Buffers;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;

#pragma warning disable CS8618 // 在退出构造函数时，不可为 null 的字段必须包含非 null 值。请考虑添加 'required' 修饰符或声明为可以为 null。
// ReSharper disable ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
#pragma warning disable CS8625 // 无法将 null 字面量转换为非 null 的引用类型。
#pragma warning disable CS8600 // 将 null 字面量或可能为 null 的值转换为非 null 类型。
#pragma warning disable CS8603 // 可能返回 null 引用。

namespace Hsenl.Network {
    public abstract class Network : Component {
        public static MessagePool MessagePool { get; set; } = new();

        private readonly NetworkPool<HBuffer> _messageCachePool = new();
        private uint _rpcId;
        private readonly ConcurrentDictionary<uint, IRpcInfo> _rpcInfos = new(); // key: rpcId

        private long _rpcTimeOutTime = 10000;
        private long _currentTimeoutTimeMin;
        private readonly Queue<uint> _timeoutQueue = new();

        private readonly HThreadTask _threadTask = new();

        public int RpcCount => this._rpcInfos.Count;

        protected override void OnDestroy() {
            this._rpcId = 0;
            this._rpcInfos.Clear();
            this._threadTask.Stop();
        }

        public abstract Service Service { get; }

        public void Notif<T>(T message, long channelId) where T : INotifMessage {
            var buffer = this.Message2Bytes(message, 0);
            this.Service.Write(channelId, buffer.AsSpan(0, buffer.Position));
            this._messageCachePool.Return(buffer);
        }

        public void Notif<T>(ref T message, long channelId) where T : INotifMessage {
            var buffer = this.Message2Bytes(message, 0);
            this.Service.Write(channelId, buffer.AsSpan(0, buffer.Position));
            this._messageCachePool.Return(buffer);
        }

        internal void SendWithRpcId<T>(T message, uint rpcId, long channelId) where T : IRpcMessage {
            this.SendWithRpcId_NoLock(message, rpcId, channelId);
        }

        internal void SendWithRpcId<T>(ref T message, uint rpcId, long channelId) where T : IRpcMessage {
            this.SendWithRpcId_NoLock(ref message, rpcId, channelId);
        }

        private void SendWithRpcId_NoLock<T>(T message, uint rpcId, long channelId) where T : IRpcMessage {
            var buffer = this.Message2Bytes(message, 4);
            buffer.AsSpan(2, 4).WriteTo(rpcId);
            this.Service.Write(channelId, buffer.AsSpan(0, buffer.Position));
            this._messageCachePool.Return(buffer);
        }

        private void SendWithRpcId_NoLock<T>(ref T message, uint rpcId, long channelId) where T : IRpcMessage {
            var buffer = this.Message2Bytes(message, 4);
            buffer.AsSpan(2, 4).WriteTo(rpcId);
            this.Service.Write(channelId, buffer.AsSpan(0, buffer.Position));
            this._messageCachePool.Return(buffer);
        }

        public RpcInfo<TRequest, TResponse> Call<TRequest, TResponse>(TRequest message, long channelId)
            where TRequest : IRpcMessage where TResponse : IRpcMessage {
            var rpcId = ++this._rpcId;
            if (this._rpcId >= uint.MaxValue - 10000)
                this._rpcId = 0;
            var rpcInfo = RpcInfo<TRequest, TResponse>.Create(TimeInfo.Now);
            this._rpcInfos.TryAdd(rpcId, rpcInfo);
            this.SendWithRpcId_NoLock(message, rpcId, channelId);
            return rpcInfo;
        }

        public RpcInfo<TRequest, TResponse> Call<TRequest, TResponse>(ref TRequest message, long channelId)
            where TRequest : IRpcMessage where TResponse : IRpcMessage {
            var rpcId = ++this._rpcId;
            if (this._rpcId >= uint.MaxValue - 10000)
                this._rpcId = 0;
            var rpcInfo = RpcInfo<TRequest, TResponse>.Create(TimeInfo.Now);
            this._rpcInfos.TryAdd(rpcId, rpcInfo);
            this.SendWithRpcId_NoLock(ref message, rpcId, channelId);
            return rpcInfo;
        }

        // 一般来说, 我们不用手动的去调用发送, 交给系统自动调用就行了, 如果频繁的发送小包, 反而会让IO性能下降
        public void StartSend(long channelId) {
            this.Service.StartSend(channelId);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private HBuffer Message2Bytes<T>(T message, int reserveSpace) where T : IMessage {
            var type = typeof(T);
            var opcode = OpcodeLookupTable.GetMessageOpcode(type);
            var buffer = this._messageCachePool.Rent();
            buffer.Seek(0, SeekOrigin.Begin);
            buffer.GetSpan(2).WriteTo(opcode);
            buffer.Advance(2);
            buffer.Advance(reserveSpace);
            NetworkSerializer.SerializeOfMemoryPack(buffer, message);
            return buffer;
        }

        protected void OnRecvMessage(long channelId, Memory<byte> data) {
            if (data.Length <= 2)
                throw new ParseMessageException($"Invalid message length {data.Length}");

            var opcode = Unsafe.ReadUnaligned<ushort>(ref MemoryMarshal.GetReference(data.Slice(0, 2).Span));
            var message = data.Slice(2); // 消息本体
            var span = message.Span;
            MessageTypeInfo messageTypeInfo;
            try {
                messageTypeInfo = OpcodeLookupTable.GetMessageTypeInfo(opcode);
            }
            catch (Exception e) {
                // 我们默认找不到opcode就是消息出问题了
                throw new ParseMessageException(e.Message);
            }

            switch (messageTypeInfo.messageKind) {
                case 1: {
                    // 这是普通消息, 走消息分发路线, 把数据原封不动的发出去, 交给终端去反序列化
                    MessageDispatcher.DispatcherNotifMessage(messageTypeInfo.type, span, this, channelId);
                    break;
                }

                case 2: {
                    // 这是个请求消息, 一样走分发路线, 只不过多带个rpcId
                    var rpcId = BitConverter.ToUInt32(span.Slice(0, 4));
                    MessageDispatcher.DispatcherRequestMessage(messageTypeInfo.type, span.Slice(4), this, channelId, rpcId);
                    break;
                }

                case 3: {
                    // 这是一个rpc的回复消息, 提取rpcId, 并交给rpcInfo处理
                    var rpcId = BitConverter.ToUInt32(span.Slice(0, 4));
                    if (this._rpcInfos.Remove(rpcId, out var rpcInfo)) {
                        var memory = message.Slice(4);
                        rpcInfo.Response(memory, this._threadTask);
                    }

                    break;
                }
            }
        }

        public void SetRpcTimeoutTime(long time) {
            this._rpcTimeOutTime = time;
        }

        protected override void OnUpdate() {
            // 清除超时的rpc消息
            var now = TimeInfo.Now;
            if (now > this._currentTimeoutTimeMin) {
                this._timeoutQueue.Clear();
                var minTime = 0L;
                foreach (var kv in this._rpcInfos) {
                    var startTime = kv.Value.StartTime;
                    if (now > startTime + this._rpcTimeOutTime) {
                        // Log.Info("Rpc timeout!");
                        this._timeoutQueue.Enqueue(kv.Key);
                        continue;
                    }
                
                    if (minTime == 0 || startTime < minTime) {
                        minTime = startTime;
                    }
                }
                
                while (this._timeoutQueue.Count > 0) {
                    this._rpcInfos.Remove(this._timeoutQueue.Dequeue(), out _);
                }
                
                this._currentTimeoutTimeMin = minTime;
            }
        }

        protected override void OnLateUpdate() {
            this.Service?.OnUpdate();
        }

        public interface IRpcInfo {
            long StartTime { get; }
            hvoid Response(Memory<byte> data, HThreadTask threadTask);
            void Dispose();
        }

        public class RpcInfo<TRequest, TResponse> : IRpcInfo where TRequest : IRpcMessage where TResponse : IRpcMessage {
            private HTask _waitTask;
            private int _waitType; // 1: 主线程, 2: 多线程

            public long StartTime { get; private set; }

            public TResponse result;
            private bool _extenResult; // 外部提供 result 壳子, 序列化的时候, 就不会新实例化对象了

            public static RpcInfo<TRequest, TResponse> Create(long startTime) {
                var rpcInfo = ObjectPool.Rent<RpcInfo<TRequest, TResponse>>();
                rpcInfo.StartTime = startTime;
                return rpcInfo;
            }

            // 从主线程返回, 所以会比下面的慢一个帧以内的时间
            public HTask Wait<T>(T msg) where T : DisposableMessage, TResponse {
                this._extenResult = true;
                this._waitType = 1;
                this.result = msg;
                this._waitTask = HTask.Create();
                return this._waitTask;
            }

            public HTask Wait() {
                this._waitType = 1;
                this._waitTask = HTask.Create();
                return this._waitTask;
            }

            public HTask WaitMultiThread<T>(T msg) where T : DisposableMessage, TResponse {
                this._extenResult = true;
                this._waitType = 2;
                this.result = msg;
                this._waitTask = HTask.Create();
                return this._waitTask;
            }

            public HTask WaitMultiThread() {
                this._waitType = 2;
                this._waitTask = HTask.Create();
                return this._waitTask;
            }


            async hvoid IRpcInfo.Response(Memory<byte> data, HThreadTask threadTask) {
                try {
                    switch (this._waitType) {
                        case 1: {
                            if (this._extenResult) {
                                var response = this.result;
                                NetworkSerializer.DeserializeOfMemoryPack(data.Span, ref response);
                            }
                            else {
                                this.result = NetworkSerializer.DeserializeOfMemoryPack<TResponse>(data.Span);
                            }

                            await HTask.SwitchToMainThread();
                            this._waitTask.SetResult();
                            break;
                        }

                        case 2: {
                            if (this._extenResult) {
                                var response = this.result;
                                NetworkSerializer.DeserializeOfMemoryPack(data.Span, ref response);
                            }
                            else {
                                this.result = NetworkSerializer.DeserializeOfMemoryPack<TResponse>(data.Span);
                            }

                            if (threadTask != null) {
                                await threadTask.SwitchToThread();
                            }

                            this._waitTask.SetResult();
                            break;
                        }
                    }
                }
                finally {
                    this.Dispose();
                }
            }

            public void Dispose() {
                this._waitTask = default;
                this._waitType = 0;
                this.result = default;
                this._extenResult = false;
                ObjectPool.Return(this);
            }
        }
    }
}